
#ifndef __OPTICAL_CLIP_DETECTOR_H
#define __OPTICAL_CLIP_DETECTOR_H

#if (__cplusplus == 201103L)
#include <unordered_map>
#include <unordered_set>
#else
#include <tr1/unordered_map>
#include <tr1/unordered_set>
#endif

#include <string>
#include <vector>
#include <numeric>
#include "opencv2/opencv.hpp"


#include <NvInfer.h>
#include <NvInferRuntime.h>
#include <cuda.h>
#include <cuda_runtime.h>


namespace optical_clip_detector
{ 
  template <typename T>
  struct TrtDestroyer
  {
      void operator()(T* t) { t->destroy(); }
  };

  template <typename T> using TrtUniquePtr = std::unique_ptr<T, TrtDestroyer<T> >;

  class Logger : public nvinfer1::ILogger {
	  void log(Severity severity, const char* msg) noexcept override {
      // suppress info-level messages
      if (severity <= Severity::kWARNING)
        std::cout << std::string(msg) << std::endl;
    }
  } logger;

  class Yolov3CLIPDetector 
  {
  public:
    Yolov3CLIPDetector();
    ~Yolov3CLIPDetector();
    bool Initialize();

    // void setParams(float aConfidenceThres,                           // Confidence threshold
    //                 std::unordered_set<std::string> aTargetClassname, // Classes need to be detected
    //                 std::string aModelFilePath,                       // Model file path
    //                 size_t aWidth,                                    // Block width to DNN
    //                 size_t aHeight);


    bool inference(cv::Mat &visible_pFrame,
                          std::vector<cv::Rect2d> &pRoi,
                          std::vector<std::string> &pName,
                          std::vector<float> &pConf
                          );


    bool initEngine(const std::string& enginePath);
    nvinfer1::ICudaEngine* getEngine(const std::string& engine);

    int modify_mode=0;
    std::vector<float> means = {0.48145466, 0.4578275, 0.40821073};
    std::vector<float> stds = {0.26862954, 0.26130258, 0.27577711};
    std::vector<std::string> target_names;
    std::vector<std::string> imagebind_class_names;
    float temperature = 10;
    float conf_threshold = 0.5;
    std::string model_path;
    std::string text_emdeding_path;
    bool first_init =false;
    float mode2_remove_threshold=0.3;
    float mode2_update_threshold = 0.5;
    float mode1_update_threshold = 0;
    float mode1_remove_threshold = 0;
    int relearning_mode = 0;
    float width_height_ratio = 4.;
    int img_padding=20;
    int imagebind_detect_order = 0;
    int  max_target_num = 4;
    int save_img_flag = 0;
    uint32_t fileSize = 0;

  private:

    //boost::shared_ptr<cognition::OpticalDetection::Resource> _YoloV3GPUDetector;
    std::vector<std::map<std::string ,std::vector<float>>> _known_embeddings;
    size_t _clip_Width=256, _clip_Height=256;
    size_t _embedding_size;
    std::string _resourceId = "";
    size_t deviceId=0;
    std::vector<std::string> cls_names;
    std::vector<float> _embeddings;
    std::vector<float> _embeddings1;

    nvinfer1::ICudaEngine* _engine;
    nvinfer1::IExecutionContext * _context;
    nvinfer1::IRuntime * runtime;
    std::vector<void*> _bindings;
    std::vector<int64_t> _buffer_size;
    cudaStream_t _stream{};
    

  };

  bool image_preprocess(const cv::Mat &src_image, int width, int height, std::vector<float> &input_vectors,std::vector<float> &means, std::vector<float> &stds);
  bool cv2vec(const cv::Mat src_image, std::vector<float> &input_vectors);
  std::map<std::string ,std::vector<float>> load_pre_text_file(std::vector<std::string> &files);
  bool load_pre_text_embeddings(std::string path, std::vector<std::map<std::string ,std::vector<float>>> &known_embeddings, std::vector<std::string> cls_names);
  float cosine_similarity(const std::vector<float>& emb1, const std::vector<float>& emb2);
  std::vector<float> softmax(std::vector<float> &conf, float temperature=100);

  int volume(const nvinfer1::Dims& d);
  inline void cudaCheck(cudaError_t ret, std::ostream& err = std::cerr)
  {
    if (ret != cudaSuccess)
    {
        err << "Cuda failure: " << cudaGetErrorString(ret) << std::endl;
        throw "engine init failure";
        //abort();
    }
  }

} // namespace optical_clip_detector

#endif
